home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.002 / stk-3 / STk-3.1 / Tk / generic / tkMessage.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-31  |  26.9 KB  |  908 lines

  1. /* 
  2.  * tkMessage.c --
  3.  *
  4.  *    This module implements a message widgets for the Tk
  5.  *    toolkit.  A message widget displays a multi-line string
  6.  *    in a window according to a particular aspect ratio.
  7.  *
  8.  * Copyright (c) 1990-1994 The Regents of the University of California.
  9.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * SCCS: @(#) tkMessage.c 1.66 96/02/15 18:52:28
  15.  */
  16.  
  17. #include "tkPort.h"
  18. #include "default.h"
  19. #include "tkInt.h"
  20.  
  21. /*
  22.  * A data structure of the following type is kept for each message
  23.  * widget managed by this file:
  24.  */
  25.  
  26. typedef struct {
  27.     Tk_Window tkwin;        /* Window that embodies the message.  NULL
  28.                  * means that the window has been destroyed
  29.                  * but the data structures haven't yet been
  30.                  * cleaned up.*/
  31.     Display *display;        /* Display containing widget.  Used, among
  32.                  * other things, so that resources can be
  33.                  * freed even after tkwin has gone away. */
  34.     Tcl_Interp *interp;        /* Interpreter associated with message. */
  35.     Tcl_Command widgetCmd;    /* Token for message's widget command. */
  36.     Tk_Uid string;        /* String displayed in message. */
  37.     int numChars;        /* Number of characters in string, not
  38.                  * including terminating NULL character. */
  39.     char *textVarName;        /* Name of variable (malloc'ed) or NULL.
  40.                  * If non-NULL, message displays the contents
  41.                  * of this variable. */
  42.  
  43.     /*
  44.      * Information used when displaying widget:
  45.      */
  46.  
  47.     Tk_3DBorder border;        /* Structure used to draw 3-D border and
  48.                  * background.  NULL means a border hasn't
  49.                  * been created yet. */
  50.     int borderWidth;        /* Width of border. */
  51.     int relief;            /* 3-D effect: TK_RELIEF_RAISED, etc. */
  52.     int highlightWidth;        /* Width in pixels of highlight to draw
  53.                  * around widget when it has the focus.
  54.                  * <= 0 means don't draw a highlight. */
  55.     XColor *highlightBgColorPtr;
  56.                 /* Color for drawing traversal highlight
  57.                  * area when highlight is off. */
  58.     XColor *highlightColorPtr;    /* Color for drawing traversal highlight. */
  59.     int inset;            /* Total width of all borders, including
  60.                  * traversal highlight and 3-D border.
  61.                  * Indicates how much interior stuff must
  62.                  * be offset from outside edges to leave
  63.                  * room for borders. */
  64.     XFontStruct *fontPtr;    /* Information about text font, or NULL. */
  65.     XColor *fgColorPtr;        /* Foreground color in normal mode. */
  66.     GC textGC;            /* GC for drawing text in normal mode. */
  67.     int padX, padY;        /* User-requested extra space around text. */
  68.     Tk_Anchor anchor;        /* Where to position text within window region
  69.                  * if window is larger or smaller than
  70.                  * needed. */
  71.     int width;            /* User-requested width, in pixels.  0 means
  72.                  * compute width using aspect ratio below. */
  73.     int aspect;            /* Desired aspect ratio for window
  74.                  * (100*width/height). */
  75.     int lineLength;        /* Length of each line, in pixels.  Computed
  76.                  * from width and/or aspect. */
  77.     int msgHeight;        /* Total number of pixels in vertical direction
  78.                  * needed to display message. */
  79.     Tk_Justify justify;        /* Justification for text. */
  80.  
  81.     /*
  82.      * Miscellaneous information:
  83.      */
  84.  
  85.     Tk_Cursor cursor;        /* Current cursor for window, or None. */
  86.     char *takeFocus;        /* Value of -takefocus option;  not used in
  87.                  * the C code, but used by keyboard traversal
  88.                  * scripts.  Malloc'ed, but may be NULL. */
  89.     int flags;            /* Various flags;  see below for
  90.                  * definitions. */
  91. } Message;
  92.  
  93. /*
  94.  * Flag bits for messages:
  95.  *
  96.  * REDRAW_PENDING:        Non-zero means a DoWhenIdle handler
  97.  *                has already been queued to redraw
  98.  *                this window.
  99.  * GOT_FOCUS:            Non-zero means this button currently
  100.  *                has the input focus.
  101.  */
  102.  
  103. #define REDRAW_PENDING        1
  104. #define GOT_FOCUS        4
  105.  
  106. /*
  107.  * Information used for argv parsing.
  108.  */
  109.  
  110. static Tk_ConfigSpec configSpecs[] = {
  111.     {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
  112.     DEF_MESSAGE_ANCHOR, Tk_Offset(Message, anchor), 0},
  113.     {TK_CONFIG_INT, "-aspect", "aspect", "Aspect",
  114.     DEF_MESSAGE_ASPECT, Tk_Offset(Message, aspect), 0},
  115.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  116.     DEF_MESSAGE_BG_COLOR, Tk_Offset(Message, border),
  117.     TK_CONFIG_COLOR_ONLY},
  118.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  119.     DEF_MESSAGE_BG_MONO, Tk_Offset(Message, border),
  120.     TK_CONFIG_MONO_ONLY},
  121.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  122.     (char *) NULL, 0, 0},
  123.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  124.     (char *) NULL, 0, 0},
  125.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  126.     DEF_MESSAGE_BORDER_WIDTH, Tk_Offset(Message, borderWidth), 0},
  127.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  128.     DEF_MESSAGE_CURSOR, Tk_Offset(Message, cursor), TK_CONFIG_NULL_OK},
  129.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  130.     (char *) NULL, 0, 0},
  131.     {TK_CONFIG_FONT, "-font", "font", "Font",
  132.     DEF_MESSAGE_FONT, Tk_Offset(Message, fontPtr), 0},
  133.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  134.     DEF_MESSAGE_FG, Tk_Offset(Message, fgColorPtr), 0},
  135.     {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
  136.     "HighlightBackground", DEF_MESSAGE_HIGHLIGHT_BG,
  137.     Tk_Offset(Message, highlightBgColorPtr), 0},
  138.     {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
  139.     DEF_MESSAGE_HIGHLIGHT, Tk_Offset(Message, highlightColorPtr), 0},
  140.     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
  141.     "HighlightThickness",
  142.     DEF_MESSAGE_HIGHLIGHT_WIDTH, Tk_Offset(Message, highlightWidth), 0},
  143.     {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
  144.     DEF_MESSAGE_JUSTIFY, Tk_Offset(Message, justify), 0},
  145.     {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
  146.     DEF_MESSAGE_PADX, Tk_Offset(Message, padX), 0},
  147.     {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
  148.     DEF_MESSAGE_PADY, Tk_Offset(Message, padY), 0},
  149.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  150.     DEF_MESSAGE_RELIEF, Tk_Offset(Message, relief), 0},
  151. #ifdef STk_CODE
  152.     {TK_CONFIG_CLOSURE, "-takefocus", "takeFocus", "TakeFocus",
  153. #else
  154.     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
  155. #endif
  156.     DEF_MESSAGE_TAKE_FOCUS, Tk_Offset(Message, takeFocus),
  157.     TK_CONFIG_NULL_OK},
  158.     {TK_CONFIG_STRING, "-text", "text", "Text",
  159.     DEF_MESSAGE_TEXT, Tk_Offset(Message, string), 0},
  160.     {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
  161.     DEF_MESSAGE_TEXT_VARIABLE, Tk_Offset(Message, textVarName),
  162.     TK_CONFIG_NULL_OK},
  163.     {TK_CONFIG_PIXELS, "-width", "width", "Width",
  164.     DEF_MESSAGE_WIDTH, Tk_Offset(Message, width), 0},
  165.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  166.     (char *) NULL, 0, 0}
  167. };
  168.  
  169. /*
  170.  * Forward declarations for procedures defined later in this file:
  171.  */
  172.  
  173. static void        MessageCmdDeletedProc _ANSI_ARGS_((
  174.                 ClientData clientData));
  175. static void        MessageEventProc _ANSI_ARGS_((ClientData clientData,
  176.                 XEvent *eventPtr));
  177. static char *        MessageTextVarProc _ANSI_ARGS_((ClientData clientData,
  178.                 Tcl_Interp *interp, char *name1, char *name2,
  179.                 int flags));
  180. static int        MessageWidgetCmd _ANSI_ARGS_((ClientData clientData,
  181.                 Tcl_Interp *interp, int argc, char **argv));
  182. static void        ComputeMessageGeometry _ANSI_ARGS_((Message *msgPtr));
  183. static int        ConfigureMessage _ANSI_ARGS_((Tcl_Interp *interp,
  184.                 Message *msgPtr, int argc, char **argv,
  185.                 int flags));
  186. static void        DestroyMessage _ANSI_ARGS_((char *memPtr));
  187. static void        DisplayMessage _ANSI_ARGS_((ClientData clientData));
  188.  
  189. /*
  190.  *--------------------------------------------------------------
  191.  *
  192.  * Tk_MessageCmd --
  193.  *
  194.  *    This procedure is invoked to process the "message" Tcl
  195.  *    command.  See the user documentation for details on what
  196.  *    it does.
  197.  *
  198.  * Results:
  199.  *    A standard Tcl result.
  200.  *
  201.  * Side effects:
  202.  *    See the user documentation.
  203.  *
  204.  *--------------------------------------------------------------
  205.  */
  206.  
  207. int
  208. Tk_MessageCmd(clientData, interp, argc, argv)
  209.     ClientData clientData;    /* Main window associated with
  210.                  * interpreter. */
  211.     Tcl_Interp *interp;        /* Current interpreter. */
  212.     int argc;            /* Number of arguments. */
  213.     char **argv;        /* Argument strings. */
  214. {
  215.     register Message *msgPtr;
  216.     Tk_Window new;
  217.     Tk_Window tkwin = (Tk_Window) clientData;
  218.  
  219.     if (argc < 2) {
  220.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  221.         argv[0], " pathName ?options?\"", (char *) NULL);
  222.     return TCL_ERROR;
  223.     }
  224.  
  225.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  226.     if (new == NULL) {
  227.     return TCL_ERROR;
  228.     }
  229.  
  230.     msgPtr = (Message *) ckalloc(sizeof(Message));
  231.     msgPtr->tkwin = new;
  232.     msgPtr->display = Tk_Display(new);
  233.     msgPtr->interp = interp;
  234.     msgPtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(msgPtr->tkwin),
  235.         MessageWidgetCmd, (ClientData) msgPtr, MessageCmdDeletedProc);
  236.     msgPtr->string = NULL;
  237.     msgPtr->numChars = 0;
  238.     msgPtr->textVarName = NULL;
  239.     msgPtr->border = NULL;
  240.     msgPtr->borderWidth = 0;
  241.     msgPtr->relief = TK_RELIEF_FLAT;
  242.     msgPtr->highlightWidth = 0;
  243.     msgPtr->highlightBgColorPtr = NULL;
  244.     msgPtr->highlightColorPtr = NULL;
  245.     msgPtr->inset = 0;
  246.     msgPtr->fontPtr = NULL;
  247.     msgPtr->fgColorPtr = NULL;
  248.     msgPtr->textGC = None;
  249.     msgPtr->padX = 0;
  250.     msgPtr->padY = 0;
  251.     msgPtr->anchor = TK_ANCHOR_CENTER;
  252.     msgPtr->width = 0;
  253.     msgPtr->aspect = 150;
  254.     msgPtr->lineLength = 0;
  255.     msgPtr->msgHeight = 0;
  256.     msgPtr->justify = TK_JUSTIFY_LEFT;
  257.     msgPtr->cursor = None;
  258.     msgPtr->takeFocus = NULL;
  259.     msgPtr->flags = 0;
  260.  
  261.     Tk_SetClass(msgPtr->tkwin, "Message");
  262.     Tk_CreateEventHandler(msgPtr->tkwin,
  263.         ExposureMask|StructureNotifyMask|FocusChangeMask,
  264.         MessageEventProc, (ClientData) msgPtr);
  265.     if (ConfigureMessage(interp, msgPtr, argc-2, argv+2, 0) != TCL_OK) {
  266.     goto error;
  267.     }
  268.  
  269. #ifdef STk_CODE
  270.     STk_sharp_dot_result(interp, Tk_PathName(msgPtr->tkwin));
  271. #else
  272.     interp->result = Tk_PathName(msgPtr->tkwin);
  273. #endif
  274.     return TCL_OK;
  275.  
  276.     error:
  277.     Tk_DestroyWindow(msgPtr->tkwin);
  278.     return TCL_ERROR;
  279. }
  280.  
  281. /*
  282.  *--------------------------------------------------------------
  283.  *
  284.  * MessageWidgetCmd --
  285.  *
  286.  *    This procedure is invoked to process the Tcl command
  287.  *    that corresponds to a widget managed by this module.
  288.  *    See the user documentation for details on what it does.
  289.  *
  290.  * Results:
  291.  *    A standard Tcl result.
  292.  *
  293.  * Side effects:
  294.  *    See the user documentation.
  295.  *
  296.  *--------------------------------------------------------------
  297.  */
  298.  
  299. static int
  300. MessageWidgetCmd(clientData, interp, argc, argv)
  301.     ClientData clientData;    /* Information about message widget. */
  302.     Tcl_Interp *interp;        /* Current interpreter. */
  303.     int argc;            /* Number of arguments. */
  304.     char **argv;        /* Argument strings. */
  305. {
  306.     register Message *msgPtr = (Message *) clientData;
  307.     size_t length;
  308.     int c;
  309.  
  310.     if (argc < 2) {
  311.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  312.         " option ?arg arg ...?\"", (char *) NULL);
  313.     return TCL_ERROR;
  314.     }
  315.     c = argv[1][0];
  316.     length = strlen(argv[1]);
  317.     if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
  318.         && (length >= 2)) {
  319.     if (argc != 3) {
  320.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  321.             argv[0], " cget option\"",
  322.             (char *) NULL);
  323.         return TCL_ERROR;
  324.     }
  325.     return Tk_ConfigureValue(interp, msgPtr->tkwin, configSpecs,
  326.         (char *) msgPtr, argv[2], 0);
  327.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
  328.         && (length  >= 2)) {
  329.     if (argc == 2) {
  330.         return Tk_ConfigureInfo(interp, msgPtr->tkwin, configSpecs,
  331.             (char *) msgPtr, (char *) NULL, 0);
  332.     } else if (argc == 3) {
  333.         return Tk_ConfigureInfo(interp, msgPtr->tkwin, configSpecs,
  334.             (char *) msgPtr, argv[2], 0);
  335.     } else {
  336.         return ConfigureMessage(interp, msgPtr, argc-2, argv+2,
  337.             TK_CONFIG_ARGV_ONLY);
  338.     }
  339.     } else {
  340.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  341.         "\": must be cget or configure", (char *) NULL);
  342.     return TCL_ERROR;
  343.     }
  344. }
  345.  
  346. /*
  347.  *----------------------------------------------------------------------
  348.  *
  349.  * DestroyMessage --
  350.  *
  351.  *    This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
  352.  *    to clean up the internal structure of a message at a safe time
  353.  *    (when no-one is using it anymore).
  354.  *
  355.  * Results:
  356.  *    None.
  357.  *
  358.  * Side effects:
  359.  *    Everything associated with the message is freed up.
  360.  *
  361.  *----------------------------------------------------------------------
  362.  */
  363.  
  364. static void
  365. DestroyMessage(memPtr)
  366.     char *memPtr;        /* Info about message widget. */
  367. {
  368.     register Message *msgPtr = (Message *) memPtr;
  369.  
  370.     /*
  371.      * Free up all the stuff that requires special handling, then
  372.      * let Tk_FreeOptions handle all the standard option-related
  373.      * stuff.
  374.      */
  375.  
  376.     if (msgPtr->textVarName != NULL) {
  377.     Tcl_UntraceVar(msgPtr->interp, msgPtr->textVarName,
  378.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  379.         MessageTextVarProc, (ClientData) msgPtr);
  380.     }
  381.     if (msgPtr->textGC != None) {
  382.     Tk_FreeGC(msgPtr->display, msgPtr->textGC);
  383.     }
  384.     Tk_FreeOptions(configSpecs, (char *) msgPtr, msgPtr->display, 0);
  385.     ckfree((char *) msgPtr);
  386. }
  387.  
  388. /*
  389.  *----------------------------------------------------------------------
  390.  *
  391.  * ConfigureMessage --
  392.  *
  393.  *    This procedure is called to process an argv/argc list, plus
  394.  *    the Tk option database, in order to configure (or
  395.  *    reconfigure) a message widget.
  396.  *
  397.  * Results:
  398.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  399.  *    returned, then interp->result contains an error message.
  400.  *
  401.  * Side effects:
  402.  *    Configuration information, such as text string, colors, font,
  403.  *    etc. get set for msgPtr;  old resources get freed, if there
  404.  *    were any.
  405.  *
  406.  *----------------------------------------------------------------------
  407.  */
  408.  
  409. static int
  410. ConfigureMessage(interp, msgPtr, argc, argv, flags)
  411.     Tcl_Interp *interp;        /* Used for error reporting. */
  412.     register Message *msgPtr;    /* Information about widget;  may or may
  413.                  * not already have values for some fields. */
  414.     int argc;            /* Number of valid entries in argv. */
  415.     char **argv;        /* Arguments. */
  416.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  417. {
  418.     XGCValues gcValues;
  419.     GC newGC;
  420.  
  421.     /*
  422.      * Eliminate any existing trace on a variable monitored by the message.
  423.      */
  424.  
  425.     if (msgPtr->textVarName != NULL) {
  426.     Tcl_UntraceVar(interp, msgPtr->textVarName, 
  427.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  428.         MessageTextVarProc, (ClientData) msgPtr);
  429.     }
  430.  
  431.     if (Tk_ConfigureWidget(interp, msgPtr->tkwin, configSpecs,
  432.         argc, argv, (char *) msgPtr, flags) != TCL_OK) {
  433.     return TCL_ERROR;
  434.     }
  435.  
  436.     /*
  437.      * If the message is to display the value of a variable, then set up
  438.      * a trace on the variable's value, create the variable if it doesn't
  439.      * exist, and fetch its current value.
  440.      */
  441.  
  442.     if (msgPtr->textVarName != NULL) {
  443.     char *value;
  444.  
  445.     value = Tcl_GetVar(interp, msgPtr->textVarName, TCL_GLOBAL_ONLY);
  446.     if (value == NULL) {
  447.         Tcl_SetVar(interp, msgPtr->textVarName, msgPtr->string,
  448.             TCL_GLOBAL_ONLY);
  449.     } else {
  450.         if (msgPtr->string != NULL) {
  451.         ckfree(msgPtr->string);
  452.         }
  453.         msgPtr->string = (char *) ckalloc((unsigned) (strlen(value) + 1));
  454.         strcpy(msgPtr->string, value);
  455.     }
  456.     Tcl_TraceVar(interp, msgPtr->textVarName,
  457.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  458.         MessageTextVarProc, (ClientData) msgPtr);
  459.     }
  460.  
  461.     /*
  462.      * A few other options need special processing, such as setting
  463.      * the background from a 3-D border or handling special defaults
  464.      * that couldn't be specified to Tk_ConfigureWidget.
  465.      */
  466.  
  467.     msgPtr->numChars = strlen(msgPtr->string);
  468.  
  469.     Tk_SetBackgroundFromBorder(msgPtr->tkwin, msgPtr->border);
  470.  
  471.     if (msgPtr->highlightWidth < 0) {
  472.     msgPtr->highlightWidth = 0;
  473.     }
  474.  
  475.     gcValues.font = msgPtr->fontPtr->fid;
  476.     gcValues.foreground = msgPtr->fgColorPtr->pixel;
  477.     newGC = Tk_GetGC(msgPtr->tkwin, GCForeground|GCFont,
  478.         &gcValues);
  479.     if (msgPtr->textGC != None) {
  480.     Tk_FreeGC(msgPtr->display, msgPtr->textGC);
  481.     }
  482.     msgPtr->textGC = newGC;
  483.  
  484.     if (msgPtr->padX == -1) {
  485.     msgPtr->padX = msgPtr->fontPtr->ascent/2;
  486.     }
  487.  
  488.     if (msgPtr->padY == -1) {
  489.     msgPtr->padY = msgPtr->fontPtr->ascent/4;
  490.     }
  491.  
  492.     /*
  493.      * Recompute the desired geometry for the window, and arrange for
  494.      * the window to be redisplayed.
  495.      */
  496.  
  497.     ComputeMessageGeometry(msgPtr);
  498.     if ((msgPtr->tkwin != NULL) && Tk_IsMapped(msgPtr->tkwin)
  499.         && !(msgPtr->flags & REDRAW_PENDING)) {
  500.     Tcl_DoWhenIdle(DisplayMessage, (ClientData) msgPtr);
  501.     msgPtr->flags |= REDRAW_PENDING;
  502.     }
  503.  
  504.     return TCL_OK;
  505. }
  506.  
  507. /*
  508.  *--------------------------------------------------------------
  509.  *
  510.  * ComputeMessageGeometry --
  511.  *
  512.  *    Compute the desired geometry for a message window,
  513.  *    taking into account the desired aspect ratio for the
  514.  *    window.
  515.  *
  516.  * Results:
  517.  *    None.
  518.  *
  519.  * Side effects:
  520.  *    Tk_GeometryRequest is called to inform the geometry
  521.  *    manager of the desired geometry for this window.
  522.  *
  523.  *--------------------------------------------------------------
  524.  */
  525.  
  526. static void
  527. ComputeMessageGeometry(msgPtr)
  528.     register Message *msgPtr;    /* Information about window. */
  529. {
  530.     char *p;
  531.     int width, inc, height, numLines;
  532.     int thisWidth, maxWidth;
  533.     int aspect, lowerBound, upperBound;
  534.  
  535.     msgPtr->inset = msgPtr->borderWidth + msgPtr->highlightWidth;
  536.  
  537.     /*
  538.      * Compute acceptable bounds for the final aspect ratio.
  539.      */
  540.  
  541.     aspect = msgPtr->aspect/10;
  542.     if (aspect < 5) {
  543.     aspect = 5;
  544.     }
  545.     lowerBound = msgPtr->aspect - aspect;
  546.     upperBound = msgPtr->aspect + aspect;
  547.  
  548.     /*
  549.      * Do the computation in multiple passes:  start off with
  550.      * a very wide window, and compute its height.  Then change
  551.      * the width and try again.  Reduce the size of the change
  552.      * and iterate until dimensions are found that approximate
  553.      * the desired aspect ratio.  Or, if the user gave an explicit
  554.      * width then just use that.
  555.      */
  556.  
  557.     if (msgPtr->width > 0) {
  558.     width = msgPtr->width;
  559.     inc = 0;
  560.     } else {
  561.     width = WidthOfScreen(Tk_Screen(msgPtr->tkwin))/2;
  562.     inc = width/2;
  563.     }
  564.     for ( ; ; inc /= 2) {
  565.     maxWidth = 0;
  566.     for (numLines = 1, p = msgPtr->string; ; numLines++)  {
  567.         if (*p == '\n') {
  568.         p++;
  569.         continue;
  570.         }
  571.         p += TkMeasureChars(msgPtr->fontPtr, p,
  572.             msgPtr->numChars - (p - msgPtr->string), 0, width, 0,
  573.             TK_WHOLE_WORDS|TK_AT_LEAST_ONE, &thisWidth);
  574.         if (thisWidth > maxWidth) {
  575.         maxWidth = thisWidth;
  576.         }
  577.         if (*p == 0) {
  578.         break;
  579.         }
  580.  
  581.         /*
  582.          * Skip spaces and tabs at the beginning of a line, unless
  583.          * they follow a user-requested newline.
  584.          */
  585.  
  586.         while (isspace(UCHAR(*p))) {
  587.         if (*p == '\n') {
  588.             p++;
  589.             break;
  590.         }
  591.         p++;
  592.         }
  593.     }
  594.  
  595.     height = numLines * (msgPtr->fontPtr->ascent
  596.         + msgPtr->fontPtr->descent) + 2*msgPtr->inset
  597.         + 2*msgPtr->padY;
  598.     if (inc <= 2) {
  599.         break;
  600.     }
  601.     aspect = (100*(maxWidth + 2*msgPtr->inset + 2*msgPtr->padX))/height;
  602.     if (aspect < lowerBound) {
  603.         width += inc;
  604.     } else if (aspect > upperBound) {
  605.         width -= inc;
  606.     } else {
  607.         break;
  608.     }
  609.     }
  610.     msgPtr->lineLength = maxWidth;
  611.     msgPtr->msgHeight = numLines * (msgPtr->fontPtr->ascent
  612.         + msgPtr->fontPtr->descent);
  613.     Tk_GeometryRequest(msgPtr->tkwin,
  614.         maxWidth + 2*msgPtr->inset + 2*msgPtr->padX, height);
  615.     Tk_SetInternalBorder(msgPtr->tkwin, msgPtr->inset);
  616. }
  617.  
  618. /*
  619.  *--------------------------------------------------------------
  620.  *
  621.  * DisplayMessage --
  622.  *
  623.  *    This procedure redraws the contents of a message window.
  624.  *
  625.  * Results:
  626.  *    None.
  627.  *
  628.  * Side effects:
  629.  *    Information appears on the screen.
  630.  *
  631.  *--------------------------------------------------------------
  632.  */
  633.  
  634. static void
  635. DisplayMessage(clientData)
  636.     ClientData clientData;    /* Information about window. */
  637. {
  638.     register Message *msgPtr = (Message *) clientData;
  639.     register Tk_Window tkwin = msgPtr->tkwin;
  640.     char *p;
  641.     int x, y, lineLength, numChars, charsLeft;
  642.  
  643.     msgPtr->flags &= ~REDRAW_PENDING;
  644.     if ((msgPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  645.     return;
  646.     }
  647.     Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), msgPtr->border, 0, 0,
  648.         Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  649.  
  650.     /*
  651.      * Compute starting y-location for message based on message size
  652.      * and anchor option.
  653.      */
  654.  
  655.     switch (msgPtr->anchor) {
  656.     case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
  657.         y = msgPtr->inset + msgPtr->padY;
  658.         break;
  659.     case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
  660.         y = ((int) (Tk_Height(tkwin) - msgPtr->msgHeight))/2;
  661.         break;
  662.     default:
  663.         y = Tk_Height(tkwin) - msgPtr->inset - msgPtr->padY
  664.             - msgPtr->msgHeight;
  665.         break;
  666.     }
  667.     y += msgPtr->fontPtr->ascent;
  668.  
  669.     /*
  670.      * Work through the string to display one line at a time.
  671.      * Display each line in three steps.  First compute the
  672.      * line's width, then figure out where to display the
  673.      * line to justify it properly, then display the line.
  674.      */
  675.  
  676.     for (p = msgPtr->string, charsLeft = msgPtr->numChars; *p != 0;
  677.         y += msgPtr->fontPtr->ascent + msgPtr->fontPtr->descent) {
  678.     if (*p == '\n') {
  679.         p++;
  680.         charsLeft--;
  681.         continue;
  682.     }
  683.     numChars = TkMeasureChars(msgPtr->fontPtr, p, charsLeft, 0,
  684.         msgPtr->lineLength, 0, TK_WHOLE_WORDS|TK_AT_LEAST_ONE,
  685.         &lineLength);
  686.     switch (msgPtr->anchor) {
  687.         case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
  688.         x = msgPtr->inset + msgPtr->padX;
  689.         break;
  690.         case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
  691.         x = ((int) (Tk_Width(tkwin) - msgPtr->lineLength))/2;
  692.         break;
  693.         default:
  694.         x = Tk_Width(tkwin) - msgPtr->inset - msgPtr->padX
  695.             - msgPtr->lineLength;
  696.         break;
  697.     }
  698.     if (msgPtr->justify == TK_JUSTIFY_CENTER) {
  699.         x += (msgPtr->lineLength - lineLength)/2;
  700.     } else if (msgPtr->justify == TK_JUSTIFY_RIGHT) {
  701.         x += msgPtr->lineLength - lineLength;
  702.     }
  703.     TkDisplayChars(msgPtr->display, Tk_WindowId(tkwin),
  704.         msgPtr->textGC, msgPtr->fontPtr, p, numChars, x, y, x, 0);
  705.     p += numChars;
  706.     charsLeft -= numChars;
  707.  
  708.     /*
  709.      * Skip blanks at the beginning of a line, unless they follow
  710.      * a user-requested newline.
  711.      */
  712.  
  713.     while (isspace(UCHAR(*p))) {
  714.         charsLeft--;
  715.         if (*p == '\n') {
  716.         p++;
  717.         break;
  718.         }
  719.         p++;
  720.     }
  721.     }
  722.  
  723.     if (msgPtr->relief != TK_RELIEF_FLAT) {
  724.     Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin), msgPtr->border,
  725.         msgPtr->highlightWidth, msgPtr->highlightWidth,
  726.         Tk_Width(tkwin) - 2*msgPtr->highlightWidth,
  727.         Tk_Height(tkwin) - 2*msgPtr->highlightWidth,
  728.         msgPtr->borderWidth, msgPtr->relief);
  729.     }
  730.     if (msgPtr->highlightWidth != 0) {
  731.     GC gc;
  732.  
  733.     if (msgPtr->flags & GOT_FOCUS) {
  734.         gc = Tk_GCForColor(msgPtr->highlightColorPtr, Tk_WindowId(tkwin));
  735.     } else {
  736.         gc = Tk_GCForColor(msgPtr->highlightBgColorPtr, Tk_WindowId(tkwin));
  737.     }
  738.     Tk_DrawFocusHighlight(tkwin, gc, msgPtr->highlightWidth,
  739.         Tk_WindowId(tkwin));
  740.     }
  741. }
  742.  
  743. /*
  744.  *--------------------------------------------------------------
  745.  *
  746.  * MessageEventProc --
  747.  *
  748.  *    This procedure is invoked by the Tk dispatcher for various
  749.  *    events on messages.
  750.  *
  751.  * Results:
  752.  *    None.
  753.  *
  754.  * Side effects:
  755.  *    When the window gets deleted, internal structures get
  756.  *    cleaned up.  When it gets exposed, it is redisplayed.
  757.  *
  758.  *--------------------------------------------------------------
  759.  */
  760.  
  761. static void
  762. MessageEventProc(clientData, eventPtr)
  763.     ClientData clientData;    /* Information about window. */
  764.     XEvent *eventPtr;        /* Information about event. */
  765. {
  766.     Message *msgPtr = (Message *) clientData;
  767.  
  768.     if (((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0))
  769.         || (eventPtr->type == ConfigureNotify)) {
  770.     goto redraw;
  771.     } else if (eventPtr->type == DestroyNotify) {
  772.     if (msgPtr->tkwin != NULL) {
  773.         msgPtr->tkwin = NULL;
  774.         Tcl_DeleteCommand(msgPtr->interp,
  775.             Tcl_GetCommandName(msgPtr->interp, msgPtr->widgetCmd));
  776.     }
  777.     if (msgPtr->flags & REDRAW_PENDING) {
  778.         Tcl_CancelIdleCall(DisplayMessage, (ClientData) msgPtr);
  779.     }
  780.     Tcl_EventuallyFree((ClientData) msgPtr, DestroyMessage);
  781.     } else if (eventPtr->type == FocusIn) {
  782.     if (eventPtr->xfocus.detail != NotifyInferior) {
  783.         msgPtr->flags |= GOT_FOCUS;
  784.         if (msgPtr->highlightWidth > 0) {
  785.         goto redraw;
  786.         }
  787.     }
  788.     } else if (eventPtr->type == FocusOut) {
  789.     if (eventPtr->xfocus.detail != NotifyInferior) {
  790.         msgPtr->flags &= ~GOT_FOCUS;
  791.         if (msgPtr->highlightWidth > 0) {
  792.         goto redraw;
  793.         }
  794.     }
  795.     }
  796.     return;
  797.  
  798.     redraw:
  799.     if ((msgPtr->tkwin != NULL) && !(msgPtr->flags & REDRAW_PENDING)) {
  800.     Tcl_DoWhenIdle(DisplayMessage, (ClientData) msgPtr);
  801.     msgPtr->flags |= REDRAW_PENDING;
  802.     }
  803. }
  804.  
  805. /*
  806.  *----------------------------------------------------------------------
  807.  *
  808.  * MessageCmdDeletedProc --
  809.  *
  810.  *    This procedure is invoked when a widget command is deleted.  If
  811.  *    the widget isn't already in the process of being destroyed,
  812.  *    this command destroys it.
  813.  *
  814.  * Results:
  815.  *    None.
  816.  *
  817.  * Side effects:
  818.  *    The widget is destroyed.
  819.  *
  820.  *----------------------------------------------------------------------
  821.  */
  822.  
  823. static void
  824. MessageCmdDeletedProc(clientData)
  825.     ClientData clientData;    /* Pointer to widget record for widget. */
  826. {
  827.     Message *msgPtr = (Message *) clientData;
  828.     Tk_Window tkwin = msgPtr->tkwin;
  829.  
  830.     /*
  831.      * This procedure could be invoked either because the window was
  832.      * destroyed and the command was then deleted (in which case tkwin
  833.      * is NULL) or because the command was deleted, and then this procedure
  834.      * destroys the widget.
  835.      */
  836.  
  837.     if (tkwin != NULL) {
  838.     msgPtr->tkwin = NULL;
  839.     Tk_DestroyWindow(tkwin);
  840.     }
  841. }
  842.  
  843. /*
  844.  *--------------------------------------------------------------
  845.  *
  846.  * MessageTextVarProc --
  847.  *
  848.  *    This procedure is invoked when someone changes the variable
  849.  *    whose contents are to be displayed in a message.
  850.  *
  851.  * Results:
  852.  *    NULL is always returned.
  853.  *
  854.  * Side effects:
  855.  *    The text displayed in the message will change to match the
  856.  *    variable.
  857.  *
  858.  *--------------------------------------------------------------
  859.  */
  860.  
  861.     /* ARGSUSED */
  862. static char *
  863. MessageTextVarProc(clientData, interp, name1, name2, flags)
  864.     ClientData clientData;    /* Information about message. */
  865.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  866.     char *name1;        /* Name of variable. */
  867.     char *name2;        /* Second part of variable name. */
  868.     int flags;            /* Information about what happened. */
  869. {
  870.     register Message *msgPtr = (Message *) clientData;
  871.     char *value;
  872.  
  873.     /*
  874.      * If the variable is unset, then immediately recreate it unless
  875.      * the whole interpreter is going away.
  876.      */
  877.  
  878.     if (flags & TCL_TRACE_UNSETS) {
  879.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  880.         Tcl_SetVar(interp, msgPtr->textVarName, msgPtr->string,
  881.             TCL_GLOBAL_ONLY);
  882.         Tcl_TraceVar(interp, msgPtr->textVarName,
  883.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  884.             MessageTextVarProc, clientData);
  885.     }
  886.     return (char *) NULL;
  887.     }
  888.  
  889.     value = Tcl_GetVar(interp, msgPtr->textVarName, TCL_GLOBAL_ONLY);
  890.     if (value == NULL) {
  891.     value = "";
  892.     }
  893.     if (msgPtr->string != NULL) {
  894.     ckfree(msgPtr->string);
  895.     }
  896.     msgPtr->numChars = strlen(value);
  897.     msgPtr->string = (char *) ckalloc((unsigned) (msgPtr->numChars + 1));
  898.     strcpy(msgPtr->string, value);
  899.     ComputeMessageGeometry(msgPtr);
  900.  
  901.     if ((msgPtr->tkwin != NULL) && Tk_IsMapped(msgPtr->tkwin)
  902.         && !(msgPtr->flags & REDRAW_PENDING)) {
  903.     Tcl_DoWhenIdle(DisplayMessage, (ClientData) msgPtr);
  904.     msgPtr->flags |= REDRAW_PENDING;
  905.     }
  906.     return (char *) NULL;
  907. }
  908.